/*
 * sockets.c
 *
 *  Created on: 12/10/2011
 *      Author: utn_so
 */

#include "sockets.h"

int AFINET_abrir_conexion(unsigned short puerto)
{
  int sockfd;
  struct sockaddr_in dirlocal;
  int resp = 1;

  dirlocal.sin_family=AF_INET;
  dirlocal.sin_port=htons(puerto);
  dirlocal.sin_addr.s_addr=htonl(INADDR_ANY);

  if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))<0)
  {
    perror("socket");
    return sockfd;
  }

  if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &resp, sizeof(int)) < 0)
	{
	  perror("setsockopt");
	  return -1;
	}

  if(bind(sockfd, (struct sockaddr*)&dirlocal, sizeof(dirlocal))<0)
  {
    perror("bind");
    return -1;
  }

  if (listen(sockfd, 5)<0)
  {
    perror("listen");
    return -1;
  }

  return sockfd;
}

int AFUNIX_abrir_conexion(char *ruta)
{
  int sockfd, tam;
  int resp = 1;
  struct sockaddr_un dirlocal;

  dirlocal.sun_family=AF_UNIX;
  strcpy(dirlocal.sun_path, ruta);
  unlink(dirlocal.sun_path); /* Elimina el socket si ya existe. */
  tam=strlen(dirlocal.sun_path) + sizeof(dirlocal.sun_family);

  if ((sockfd=socket(AF_UNIX, SOCK_STREAM, 0))<0)
  {
    perror("socket");
    return sockfd;
  }

  if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &resp, sizeof(int)) < 0)
  {
	  perror("setsockopt");
	  return -1;
  }

  if(bind(sockfd, (struct sockaddr*)&dirlocal, tam)<0)
  {
    perror("bind");
    return -1;
  }

  if (listen(sockfd, 5)<0)
  {
    perror("listen");
    return -1;
  }

  return sockfd;
}

int aceptar_conexion(int sockfd)
{
  int fd;
  unsigned int tam;
  struct sockaddr cliente;

  tam=sizeof(struct sockaddr);

  if ((fd=accept(sockfd, &cliente, &tam))<0)
  {
    perror("accept");
    return fd;
  }

  return fd;
}

int AFINET_conectar(char *dirIP, unsigned short puerto)
{
  int sockfd;
  struct sockaddr_in dirserver;

  dirserver.sin_family=AF_INET;
  dirserver.sin_port=htons(puerto);
  dirserver.sin_addr.s_addr=inet_addr(dirIP);

  sockfd=socket(AF_INET, SOCK_STREAM, 0);

  if(connect(sockfd, (struct sockaddr*)&dirserver, sizeof(dirserver))<0)
  {
    perror("connect");
    return -1;
  }

  return sockfd;
}

int AFUNIX_conectar(char *ruta)
{
  int sockfd, tam;
  struct sockaddr_un dirserver;

  dirserver.sun_family = AF_UNIX;
  strcpy(dirserver.sun_path, ruta);

  tam = sizeof(dirserver.sun_family) + strlen(dirserver.sun_path);

  if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  {
	  perror("socket");
	  return -1;
  }

  if(connect(sockfd, (struct sockaddr*)&dirserver, tam)<0)
  {
    perror("connect");
    return -1;
  }

  return sockfd;
}

int NIPC_enviar(int sock, t_NIPC paquete)
{
	int ret; /* Para lo que retornan las funciones de sockets. */
	int tam;
	char *buffer;

	tam = sizeof(paquete.DescriptorID) + sizeof(paquete.PayloadDescriptor) + sizeof(paquete.PayloadLength) + paquete.PayloadLength;
	buffer = serializar(paquete);

	ret = sendall(sock, buffer, &tam);

	free(buffer);

	if(ret < 0)
		return -1;
	else return tam;
}

int NIPC_recibir(int sock, t_NIPC *paquete,int32_t *nroSector)
{
	int tam_paquete = 0, recibidos, max_buf = SOCK_BUFFER;
	char *buffer = (char *)malloc(SOCK_BUFFER);

	/* Intenta recibir el paquete entero. Si no hay espacio en el buffer, pide más memoria y vuelve a intentar
	 * hasta que haya espacio */
	while((tam_paquete = recv(sock, buffer, max_buf, MSG_PEEK)) == max_buf)
	{
		max_buf += SOCK_BUFFER;
		buffer = realloc(buffer, max_buf);
		/*log_write(archLog, config.nomProceso, INFO, "max_buf = %d, tam_paquete = %d", max_buf, tam_paquete);*/
	}

	/*log_write(archLog, config.nomProceso, INFO, "max_buf = %d, tam_paquete = %d", max_buf, tam_paquete);*/

	recibidos = recv(sock, buffer, tam_paquete, 0);

	/*if(recibidos != tam_paquete)
	{
		log_write(archLog,config.nomProceso, ERROR, "Se leyeron %d pero deberían haberse leido %d", recibidos, tam_paquete);
	}*/

	if(tam_paquete > 0)
	{
		*paquete = deserializar(buffer,nroSector);
		free(buffer);
	}

	/*if(tam_paquete != sizeof(paquete->DescriptorID) + sizeof(paquete->PayloadDescriptor) + sizeof(paquete->PayloadLength) + paquete->PayloadLength)
		log_write(archLog, config.nomProceso, ERROR, "No se leyó la cantidad necesaria de información");*/

	return tam_paquete;
}

void obtenerDescriptor(char *ID)
{
	time_t numero;
	numero = time(NULL);
	sprintf(ID, "%d%d", (int)numero, rand());
	numero++;
}

t_NIPC NIPC_crearpaquete(char *DescriptorID, char PayloadDescriptor, int PayloadLength, char *Payload,int32_t *nroSector)
{
	t_NIPC ret;

	/* Si el descriptor ID recibido es NULL genero uno nuevo, de lo contrario uso el que llega como argumento */
	if(DescriptorID == NULL)
		obtenerDescriptor(ret.DescriptorID);
	else
		memcpy(ret.DescriptorID, DescriptorID, sizeof ret.DescriptorID);
	ret.PayloadDescriptor = PayloadDescriptor;
	ret.PayloadLength = PayloadLength;

	if(PayloadLength > 0)
	{
		ret.payload = (char *)malloc(ret.PayloadLength + sizeof(int32_t));
		memcpy(ret.payload, Payload, ret.PayloadLength);
		memcpy(ret.payload + ret.PayloadLength,nroSector,sizeof(int32_t));
	}
	else
	{
		ret.payload = (char *)malloc(sizeof(int32_t));
		memcpy(ret.payload,nroSector,sizeof(int32_t));
	}
	return ret;
}

void NIPC_destruirpaquete(t_NIPC *paquete)
{
	if(paquete->PayloadLength > 0)
	{
		free(paquete->payload);
		paquete->payload = NULL;
	}
}

char *serializar(t_NIPC paquete)
{
	char *buffer = malloc(sizeof(paquete.DescriptorID) + sizeof(paquete.PayloadDescriptor) + sizeof(paquete.PayloadLength) + paquete.PayloadLength);
	int i, pos_payload = sizeof(paquete.DescriptorID) + sizeof(paquete.PayloadDescriptor) + sizeof(paquete.PayloadLength);
	int offset;

	// Copio los primeros tres campos.
	memcpy(buffer, paquete.DescriptorID, sizeof(paquete.DescriptorID));
	offset = sizeof(paquete.DescriptorID);
	memcpy(buffer + offset, &(paquete.PayloadDescriptor), sizeof(paquete.PayloadDescriptor));
	offset += sizeof(paquete.PayloadDescriptor);
	memcpy(buffer + offset, &(paquete.PayloadLength), sizeof(paquete.PayloadLength));

	// Copio el payload, sólo si PayloadLength es mayor a 0
	if(paquete.PayloadLength > 0)
	{
		for(i = 0; i < paquete.PayloadLength; i++)
		{
			buffer[i + pos_payload] = paquete.payload[i];
		}
	}

	return buffer;
}

t_NIPC deserializar(char *buffer,int32_t *nroSector)
{
	t_NIPC paquete;
	int offset;

	memcpy(paquete.DescriptorID, buffer, sizeof(paquete.DescriptorID));
	offset = sizeof(paquete.DescriptorID);
	paquete.PayloadDescriptor = *(buffer + offset);
	offset += sizeof(paquete.PayloadDescriptor);
	paquete.PayloadLength = *((int *)(buffer + offset));
	offset += sizeof(paquete.PayloadLength);

	if(paquete.PayloadLength > 0)
	{
		paquete.payload = (char *)malloc(paquete.PayloadLength +sizeof(int32_t) );
		memcpy(paquete.payload, buffer + offset, paquete.PayloadLength);
		memcpy(nroSector,paquete.payload + paquete.PayloadLength, sizeof(int32_t));
	}else
	{
		paquete.payload = (char *)malloc(sizeof(int32_t));
		memcpy(nroSector,paquete.payload, sizeof(int32_t));
	}

	return paquete;
}

int sendall(int s, char *buf, int *len)
{
	int total = 0;        // cuántos bytes hemos enviado
	int bytesleft = *len; // cuántos se han quedado pendientes
	int n;

	while(total < *len) {
		n = send(s, buf+total, bytesleft, MSG_NOSIGNAL);
		if (n == -1) { break; }
		total += n;
		bytesleft -= n;
	}

	*len = total; // devuelve aquí la cantidad enviada en realidad

	return n==-1?-1:0; // devuelve -1 si hay fallo, 0 en otro caso
}
